/*
    日志器模块:
    1.抽象日志器基类
    2.派生出不同的子类(同步日志器子类和异步日志器子类)
*/

#ifndef __L_LOGGER_H__
#define __L_LOGGER_H__

#ifndef _GUN_SOURCE
#define _GUN_SOURCE
#endif
#include <iostream>
#include <atomic>
#include <string>
#include <vector>
#include <mutex>
#include <cstdarg>
#include <unordered_map>
#include "util.hpp"
#include "message.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "level.hpp"
#include "looper.hpp"
#include "buffer.hpp"

namespace Log
{
    //抽象日志器基类
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &loggerName, LogLevel::value limitLevel, std::vector<LogSink::ptr> &sinks, Formatter::ptr formatter)
        : _loggerName(loggerName), _limitLevel(limitLevel), _sinks(sinks.begin(), sinks.end()), _formatter(formatter)
        {}

        //日志器的名称
        const std::string& name()
        {
            return _loggerName;
        }

        // 完成日志消息对象的构造过程并进行格式化,得到格式化后的日志消息字符串 ---然后进行落地输出;
        void debug(const std::string &file, size_t line, const char *format, ...)
        {
            //通过传入的参数构造出一个日志消息对象, 并进行日志的格式化, 最后将日志落地
            //1.判断该操作的日志等级与日志输出限制等级的大小关系
            if(LogLevel::value::DEBUG < _limitLevel)
            {
                return;
            }
            //2.根据传入的格式化字符串format, 取出可变参数
            va_list ap;
            va_start(ap, format);
            serialize(LogLevel::value::DEBUG, file, line, format, ap);
        }

        void info(const std::string &file, size_t line, const char *format, ...)
        {
            //1.判断该操作的日志等级与日志输出限制等级的大小关系
            if(LogLevel::value::INFO < _limitLevel)
            {
                return;
            }
            //2.取出可变参数
            va_list ap;
            va_start(ap, format);
            serialize(LogLevel::value::INFO, file, line, format, ap);
        }

        void warnning(const std::string &file, size_t line, const char *format, ...)
        {
            // 1.判断该操作的日志等级与日志输出限制等级的大小关系
            if (LogLevel::value::WARNNING < _limitLevel)
            {
                return;
            }
            // 2.取出可变参数
            va_list ap;
            va_start(ap, format);
            serialize(LogLevel::value::WARNNING, file, line, format, ap);
        }

        void error(const std::string &file, size_t line, const char *format, ...)
        {
            // 1.判断该操作的日志等级与日志输出限制等级的大小关系
            if (LogLevel::value::ERROR < _limitLevel)
            {
                return;
            }
            // 2.取出可变参数
            va_list ap;
            va_start(ap, format);
            serialize(LogLevel::value::ERROR, file, line, format, ap);
        }

        void fatal(const std::string &file, size_t line, const char *format, ...)
        {
            // 1.判断该操作的日志等级与日志输出限制等级的大小关系
            if (LogLevel::value::FATAL < _limitLevel)
            {
                return;
            }
            // 2.取出可变参数
            va_list ap;
            va_start(ap, format);
            serialize(LogLevel::value::FATAL, file, line, format, ap);
        }
    protected:
        // 抽象的接口log完成实际的日志落地输出, 不同的日志器会有不同的落地方式;
        virtual void log(const char *data, size_t len) = 0;

        //对日志进行组织和落地
        void serialize(LogLevel::value level, const std::string& file, size_t line, const char* format, va_list ap)
        {
            char *res;
            int ret = vasprintf(&res, format, ap);
            if (ret == -1)
            {
                std::cout << "vasprintf fail!" << std::endl;
            }
            va_end(ap);
            // 3.构造日志消息对象
            LogMessage msg(level, file, line, res, _loggerName);
            // 4.对日志消息进行格式化
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 5.对日志消息进行落地
            log(ss.str().c_str(), ss.str().size());
            // vasprintf函数会动态申请一块内存, 将可变参数包以格式化字符串的形式放到申请的内存中, 所以使用结束后需要我们自己使用这块内存;
            free(res);
        }

    protected:
        std::mutex _mutex;  //多线程场景下, 向同一个落地位置写入会有多执行访问导致数据不一致的问题
        std::string _loggerName; // 日志器的命令, 用来表示唯一的日志器
        // 日志输出的限制等级, 在多线程场景下会有多个线程来修改日志等级;由于实际上是一个整形类型,加锁比较慢所以设置为原子类型
        std::atomic<LogLevel::value> _limitLevel;
        std::vector<LogSink::ptr> _sinks;   //一个日志器可能会有多种落地方式, 在数组中保存不同的落地方式
        Formatter::ptr _formatter;
    };

    //同步日志器
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &loggerName, LogLevel::value limitLevel, std::vector<LogSink::ptr> &sinks, Formatter::ptr formatter)
        :Logger(loggerName, limitLevel, sinks, formatter)
        {}

    private:
        void log(const char* data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex); // 保证写日志不会出现不同日志混在一起;
            if (_sinks.empty() == false)
            {
                for(const auto& sink : _sinks)
                {
                    sink->Log(data, len);
                }
            }
        }
    };

    //异步日志器
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &loggerName, LogLevel::value limitLevel, std::vector<LogSink::ptr> &sinks, Formatter::ptr formatter, BufferType looperType)
        :Logger(loggerName, limitLevel, sinks, formatter),
        _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looperType))
        {}

        //实际日志落地函数
        void realLog(Buffer& buffer)
        {
            //异步工作线程只有一个, 就是串行化的, 不会有线程安全问题
            if (_sinks.empty() == false)
            {
                for (const auto &sink : _sinks)
                {
                    sink->Log(buffer.begin(), buffer.readAbleSize());
                }
            }
        }
    private:
        void log(const char* data, size_t len) override
        {
            // 下面的push操作本身是线程安全的, 所以这里我们就不需要加锁了;
            _looper->push(data, len);
        }

    private:
        AsyncLooper::ptr _looper;
    };

/*
    由于同步日志器的实例化过程过于复杂, 可以使用建造者模式先将一个个的零部件构造好, 再去构造同步日志器;
    使用建造者模式来建造日志器, 而不要让用户直接去构造日志器, 简化用户的使用复杂度;
    1.先抽象一个日志器建造者类---完成日志器对象所需零部件的构建 & 日志器的构建
        1.1设置日志器类型 --- 通过不同的类型去建造不同的日志器(同步日志器/异步日志器)
        1.2将不同类型日志器的创建放到同一个日志器建造类中完成;
    2.派生出具体的建造者类---这里根据作用范围划分为: 局部日志器的建造者 & 全局的日志器建造者(后边添加了全局单例管理器之后, 将日志器添加到全局单例管理器中);
    由于我们日志器的建造过程中零部件的构造没有顺序性,所以将建造者模式简化为没有指挥者类的建造者模式;
*/
enum class LoggerType
{
    LOGGER_SYNC,
    LOGGER_ASYNC
};

    //抽象日志器建造者父类
    class LoggerBuilder
    {
    public:
        //默认生成同步日志器, 日志的默认限制等级为debug
        LoggerBuilder()
        :_loggerType(LoggerType::LOGGER_SYNC), 
        _limitLevel(LogLevel::value::DEBUG),
        _looperType(BufferType::STATICSIZE)
        {}
        void buildLoggerType(LoggerType loggerType)
        {
            _loggerType = loggerType;
        }
        void buildEnableDynamicAsync(BufferType looperType)
        {
            _looperType = looperType;
        }
        void buildLoggerName(const std::string &loggerName)
        {
            _loggerName = loggerName;
        }
        void buildLimitLevel(LogLevel::value limitLevel)
        {
            _limitLevel = limitLevel;
        }
        template <class SinkType, class... Arags>
        void buildSinks(Arags &&...args)
        {
            LogSink::ptr sink = SinkFactory::create<SinkType>(std::forward<Arags>(args)...);
            _sinks.push_back(sink);
        }
        void buildFormatter(const std::string &pattern)
        {
            Formatter::ptr formatter = std::make_shared<Formatter>(pattern);
            _formatter = formatter;
        }
        virtual Logger::ptr build() = 0;

    protected:
        BufferType _looperType;
        LoggerType _loggerType; // 建造的日志类型
        std::string _loggerName;
        LogLevel::value _limitLevel;
        std::vector<LogSink::ptr> _sinks;
        Formatter::ptr _formatter;
    };

    //2.派生出局部日志器建造子类
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            //日志器的名称是用来表示日志器的唯一性的,所以必须要有
            if(_loggerName.empty())
            {
                std::cout << "日志器的名称不能为空!" << std::endl;
                abort();
            }
            //如果用户没有自己构造formatter对象, 我们就自己默认构造一个
            if(_formatter.get() == nullptr)
            {
                Formatter::ptr formatter = std::make_shared<Formatter>();
                _formatter = formatter;
            }
            //如果日志落地方式没有建造, 我们就默认建造一个标准输出的落地方式
            if(_sinks.empty())
            {
                buildSinks<StdoutLogSink>();
            }
            if(_loggerType == LoggerType::LOGGER_ASYNC)
            {
                //构建异步日志器
                return std::make_shared<AsyncLogger>(_loggerName, _limitLevel, _sinks, _formatter, _looperType);
            }

            //走到这里是构造同步日志器
            return std::make_shared<SyncLogger>(_loggerName, _limitLevel, _sinks, _formatter);
        }
    };


    //日志器的管理类
    class LoggerManager
    {
    public:
        //单例模式的日志器管理类对外提供的接口
        static LoggerManager& getInstance()
        {
            //c++11之后, 针对静态局部变量, 编译器在编译的层面实现了线程安全
            //当静态局部变量在没有构造完成之前, 其他的线程进入就会阻塞
            static LoggerManager loggerManager;
            return loggerManager;
        }

        //往管理数组中添加日志器
        void addLogger(Logger::ptr &logger) 
        {
            //为了防止重复添加同一个日志器, 在添加日志器之前先判断是否添加过了
            if(hasLogger(logger->name()))
            {
                return;
            }
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        };

        //判断是否管理了指定名称的日志器
        bool hasLogger(const std::string& loggerName)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(loggerName);
            if(it == _loggers.end())
            {
                return false;
            }
            return true;
        }

        //获取指定名称的日志器
        Logger::ptr getLogger(const std::string& loggerName)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(loggerName);
            if(it == _loggers.end())
            {
                std::cout << "没有找到" << loggerName << "日志器" << std::endl;
                return Logger::ptr();
            }
            return it->second;
        }

        //获取默认的日志器
        Logger::ptr rootLogger()
        {
            return _rootLogger;
        }

    private:
        LoggerManager()
        {
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _rootLogger = builder->build();
            
            //由于是单例模式下的初始化, 所以往hash表中添加日志器不需要加锁的
            _loggers.insert(std::make_pair("root", _rootLogger));
        }
        LoggerManager(const LoggerManager &loggerManager) = delete;
        LoggerManager &operator=(const LoggerManager &loggerManager) = delete;
    private:
        std::mutex _mutex;                  //保证管理数组线程安全的互斥锁
        Logger::ptr _rootLogger;            // 默认的日志器
        std::unordered_map<std::string, Logger::ptr> _loggers; // 用哈希表来管理日志器, 便于查找;
    };

    //设计一个全局日志器的建造者 ---- 在局部日志器建造者的基础上增加一个功能:将日志器添加到单例对象中
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            if(_loggerName.empty())
            {
                std::cout << "日志器的名称不能为空!" << std::endl;
                abort();
            }
            if(_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if(_sinks.empty())
            {
                buildSinks<StdoutLogSink>();
            }
            Logger::ptr logger;
            if (_loggerType == LoggerType::LOGGER_ASYNC)
            {
                //构建一个全局的异步日志器
                logger = std::make_shared<AsyncLogger>(_loggerName, _limitLevel, _sinks, _formatter, _looperType);
                LoggerManager::getInstance().addLogger(logger);
            }
            else
            {
                //构建一个全局的同步日志器
                logger = std::make_shared<SyncLogger>(_loggerName, _limitLevel, _sinks, _formatter);
                LoggerManager::getInstance().addLogger(logger);
            }
            return logger;
        }
    };
}

#endif

